package com.aceitunaproject.server.equipment;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.aceitunaproject.server.combat.CombatEffects;
import com.aceitunaproject.server.terrain.MovementEffects;
import com.aceitunaproject.server.terrain.TerrainEquipmentCombo;
import com.aceitunaproject.server.terrain.TerrainType;
import com.aceitunaproject.server.specialability.SpecialAbility;

/**
 * 
 * Represents a type of equipment in an unit's equipment list, ex: Sword or
 * Pointy Thing
 * 
 * @author Federico Bergstein
 */
public class Equipment {

	/**
	 * the map of combat effects for this equipment depending on the terrain
	 */
	private Map<TerrainType, CombatEffects> terrainCombatEffects = new HashMap<TerrainType, CombatEffects>();

	/**
	 * the map of movement effects for this equipment depending on the terrain
	 */
	private Map<TerrainType, MovementEffects> terrainMovementEffects = new HashMap<TerrainType, MovementEffects>();

	/**
	 * the map of combat effects by terrain and the opposing unit's equipment
	 */
	private Map<TerrainEquipmentCombo, CombatEffects> terrainEquipmentCombinationCombatEffects = new HashMap<TerrainEquipmentCombo, CombatEffects>();

	/**
	 * equipmentCombatEffects map of combat effects depending on the opposing
	 * unit's equipment
	 */
	private Map<Equipment, CombatEffects> equipmentCombatEffects = new HashMap<Equipment, CombatEffects>();

	/**
	 * the Equipment "superclass" IE for Sword's father is Pointy Thing
	 */
	private Equipment father;

	/**
	 * the equipment's name
	 */
	private String name;

	/**
	 * the equipment's basic combatEffects
	 */
	private CombatEffects basicCombatEffects;

	/**
	 * List of special abilities available to units with this equipment
	 */
	private List<SpecialAbility> specialAbilities = new LinkedList<SpecialAbility>();

	/**
	 * Class constructor
	 * 
	 * @param basicCombatEffects
	 * 			the equipment's basic combatEffects
	 * 
	 * @param name
	 *            the equipment's name
	 * @param terrainCombatEffects
	 *            the map of combat effects for this equipment depending on the
	 *            terrain
	 * @param terrainMovementEffects
	 *            the map of movement effects for this equipment depending on
	 *            the terrain
	 * @param terrainEquipmentCombinationCombatEffects
	 *            the map of combat effects by terrain and the opposing unit's
	 *            equipment
	 * @param equipmentCombatEffects
	 *            map of combat effects depending on the opposing unit's
	 *            equipment
	 * @param father
	 *            the Equipment "superclass" IE Sword's father is Pointy Thing
	 */
	public Equipment(
			CombatEffects basicCombatEffects,
			String name,
			Map<TerrainType, CombatEffects> terrainCombatEffects,
			Map<TerrainType, MovementEffects> terrainMovementEffects,
			Map<TerrainEquipmentCombo, CombatEffects> terrainEquipmentCombinationCombatEffects,
			Map<Equipment, CombatEffects> equipmentCombatEffects,
			Equipment father) {

		this(basicCombatEffects,name, terrainCombatEffects, terrainMovementEffects,
				terrainEquipmentCombinationCombatEffects,
				equipmentCombatEffects, father,
				new LinkedList<SpecialAbility>());
	}

	/**
	 * Class constructor
	 * 
	 * @param basicCombatEffects
	 * 			the equipment's basic combatEffects
	 * @param name
	 *            the equipment's name
	 * @param terrainCombatEffects
	 *            the map of combat effects for this equipment depending on the
	 *            terrain
	 * @param terrainMovementEffects
	 *            the map of movement effects for this equipment depending on
	 *            the terrain
	 * @param terrainEquipmentCombinationCombatEffects
	 *            the map of combat effects by terrain and the opposing unit's
	 *            equipment
	 * @param equipmentCombatEffects
	 *            map of combat effects depending on the opposing unit's
	 *            equipment
	 * @param father
	 *            the Equipment "superclass" IE Sword's father is Pointy Thing
	 * @param specialAbilities
	 *            the List of special abilities available to units with this
	 *            equipment
	 */
	public Equipment(CombatEffects basicCombatEffects,
			String name,
			Map<TerrainType, CombatEffects> terrainCombatEffects,
			Map<TerrainType, MovementEffects> terrainMovementEffects,
			Map<TerrainEquipmentCombo, CombatEffects> terrainEquipmentCombinationCombatEffects,
			Map<Equipment, CombatEffects> equipmentCombatEffects,
			Equipment father, List<SpecialAbility> specialAbilities) {

		
		this.basicCombatEffects = basicCombatEffects;
		
		this.specialAbilities = specialAbilities != null ? specialAbilities
				: new LinkedList<SpecialAbility>();

		this.name = name;

		if (terrainCombatEffects != null)
			this.terrainCombatEffects = terrainCombatEffects;

		if (terrainMovementEffects != null)
			this.terrainMovementEffects = terrainMovementEffects;

		if (terrainEquipmentCombinationCombatEffects != null)
			this.terrainEquipmentCombinationCombatEffects = terrainEquipmentCombinationCombatEffects;

		if (equipmentCombatEffects != null)
			this.equipmentCombatEffects = equipmentCombatEffects;

		this.father = father;

	}

	/**
	 * Gets the combats effects on an unit with this equipment in a given
	 * terrain type
	 * 
	 * @param terrainType
	 *            the Terrain Type
	 * @return the CombatEffects
	 */
	public CombatEffects getCombatEffects(TerrainType terrainType) {

		CombatEffects result = terrainCombatEffects.get(terrainType);
		if (result == null)
			result = new CombatEffects(0, 0, 0, 0);

		if (father != null)
			result = CombatEffects.add(result, father
					.getCombatEffects(terrainType));

		return result;
	}

	/**
	 * Gets the combats effects on an unit with this equipment in a given
	 * terrain type against an enemy equipped with a given equipment *
	 * 
	 * @param terrainType
	 *            the Terrain Type
	 * @param enemyEquipment
	 *            the enemy's equipment
	 * @return the CombatEffects
	 */
	public CombatEffects getCombatEffects(TerrainType terrainType,
			Equipment enemyEquipment) {
		// loops through the Enemy equipment and his ancestors
		CombatEffects result = new CombatEffects(0, 0, 0, 0);
		do {
			result = CombatEffects.add(result,
					getEquipmentTerrainCombatEffects(terrainType,
							enemyEquipment));
			enemyEquipment = enemyEquipment.getFather();
		} while (enemyEquipment != null);
		return result;

	}

	/**
	 * Gets the combats effects on an unit with this equipment in a given
	 * terrain type against an enemy equipped with a given equipment (but NOT
	 * the effects of the enemy equipment's ancestors)
	 * 
	 * @param terrainType
	 *            the Terrain Type
	 * @param enemyEquipment
	 *            the enemy's equipment
	 * @return the CombatEffects
	 */
	private CombatEffects getEquipmentTerrainCombatEffects(
			TerrainType terrainType, Equipment enemyEquipment) {
		TerrainEquipmentCombo combo = new TerrainEquipmentCombo(terrainType,
				enemyEquipment);
		CombatEffects result = terrainEquipmentCombinationCombatEffects
				.get(combo);
		if (result == null)
			result = new CombatEffects(0, 0, 0, 0);

		if (father != null)
			result = CombatEffects.add(result, father
					.getEquipmentTerrainCombatEffects(terrainType,
							enemyEquipment));

		return result;
	}

	/**
	 * Gets the movement effects for this equipment in a given Terrain Type
	 * 
	 * @param terrainType
	 *            the terrain type
	 * @return the movement effects
	 */
	public MovementEffects getMovementEffects(TerrainType terrainType) {

		MovementEffects result = terrainMovementEffects.get(terrainType);

		if (result == null)
			result = new MovementEffects();

		if (father != null)
			result = MovementEffects.add(result, father
					.getMovementEffects(terrainType));

		return result;
	}

	/**
	 * Gets the terrain-independent combat effects on an unit with this
	 * equipment against an unit with a given equipment
	 * 
	 * @param enemyEquipment
	 *            the enemy's equipment
	 * @return the CombatEffects
	 */
	public CombatEffects getCombatEffects(Equipment enemyEquipment) {

		// loops through the Enemy equipment and his ancestors
		CombatEffects result = new CombatEffects(0, 0, 0, 0);
		do {
			result = CombatEffects.add(result,
					getEquipmentCombatEffects(enemyEquipment));
			enemyEquipment = enemyEquipment.getFather();
		} while (enemyEquipment != null);
		return result;
	}

	/**
	 * returns the name of this equipment
	 * 
	 * @return the name of this equipment
	 */
	public String getName() {
		return name;
	}

	/**
	 * returns this equipment's "superclass"
	 * 
	 * @return this equipment's "superclass"
	 */
	public Equipment getFather() {
		return father;
	}

	/**
	 * returns the effect of an enemy equipment(but NOT the enemy's equipment
	 * ancestors) on an unit with this equipment,
	 * 
	 * @param enemyEquipment
	 *            the enemy equipment
	 * @return the combat effects
	 */
	private CombatEffects getEquipmentCombatEffects(Equipment enemyEquipment) {
		CombatEffects result = equipmentCombatEffects.get(enemyEquipment);

		if (result == null)
			result = new CombatEffects(0, 0, 0, 0);

		if (father != null)
			result = CombatEffects.add(result, father
					.getEquipmentCombatEffects(enemyEquipment));
		return result;
	}

	/**
	 * Adds another Equipment and combat effects to the relations of this
	 * equipment with others
	 * 
	 * @param equipment
	 *            the enemy Equipment to relate
	 * @param combatEffects
	 *            the combat effects when fighting units with that Equipment
	 * @return true if the equipment has been added, false if the equipment had
	 *         been previously added to the relationships.
	 */
	public boolean relateToEquipment(Equipment equipment,
			CombatEffects combatEffects) {

		boolean returnValue = false;
		if (!equipmentCombatEffects.containsKey(equipment)) {
			equipmentCombatEffects.put(equipment, combatEffects);
			returnValue = true;
		}
		return returnValue;
	}

	/**
	 * Adds another Equipment and combat effects to the relations of this
	 * equipment with others in the given terrain type.
	 * 
	 * @param equipment
	 *            the enemy Equipment to relate
	 * @param combatEffects
	 *            the combat effects when fighting units with that Equipment
	 * @param terrainType
	 *            the terrainType where this relationship is applicable
	 * @return true if the equipment has been added, false if the equipment had
	 *         been previously added to the relationships.
	 * 
	 */
	public boolean relateToEquipmentInTerrainType(Equipment equipment,
			TerrainType terrainType, CombatEffects combatEffects) {

		boolean returnValue = false;

		TerrainEquipmentCombo combo = new TerrainEquipmentCombo(terrainType,
				equipment);

		if (!terrainEquipmentCombinationCombatEffects.containsKey(combo)) {
			terrainEquipmentCombinationCombatEffects.put(combo, combatEffects);
			returnValue = true;
		}
		return returnValue;
	}

	/**
	 * List of special abilities available to units with this equipment
	 * 
	 * @return the list of abilities
	 */
	public List<SpecialAbility> getSpecialAbilities() {
		return specialAbilities;
	}

	/**
	 * @param basicCombatEffects
	 *            the basicCombatEffects to set
	 */
	public void setBasicCombatEffects(CombatEffects basicCombatEffects) {
		this.basicCombatEffects = basicCombatEffects;
	}

	/**
	 * @return the basicCombatEffects
	 */
	public CombatEffects getBasicCombatEffects() {
		return basicCombatEffects;
	}

}
